﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
using MvcMovie.Middleware;
using MvcMovie.Repository;
using SignalRChat.Hubs;
using Microsoft.Extensions.Logging;

namespace MvcMovie
{
    //全局配置信息
    public class Startup
    {
        private readonly IHostingEnvironment _env;
        private readonly ILoggerFactory _loggerFactory;

        public IConfiguration Configuration { get; }
        //首先在构造函数中初始化基础配置信息
        public Startup(IConfiguration configuration, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            //初始化后的配置信息绑定到一个Configuration属性上，以便另外两个方法在后期进行使用。
            //如果你要在其它类中进行使用，则需要将该实例保存到其它地方
            Configuration = configuration;
            _env = env;
            _loggerFactory = loggerFactory;

        }

        


        //依赖注入的核心，在方法的传入参数services中，首先保存了默认依赖注入里定义的类型定义，然后我们在这个方法里，可以继续注册依赖注入的类型定义
        //提前将依赖注入所需要的类型及映射关系都注册到依赖注入管理系统中，即IServiceCollection集合
        //同时一些重要的功能需要开启的话，也需要在这里开启
        //原因是因为，在新版的ASP.NET 5中，除了最基础的模块以外，绝大部分模块都是纯组件化的，这里称为Middleware，
        //而组件在使用的时候首先需要先添加该模块才能使用。
        public void ConfigureServices(IServiceCollection services)   
        {
            services.Configure<CookiePolicyOptions>(options =>  //注册的用法，普通类要保证有无参数构造函数
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            // 该方法必须在app.UseMvc之前调用
            services.AddDistributedMemoryCache();
            //services.AddSession(configure: s => { s.IdleTimeout = TimeSpan.FromMinutes(30); });
            services.AddSession(options =>
            {
                // Set a short timeout for easy testing.
                options.IdleTimeout = TimeSpan.FromSeconds(30);
                options.Cookie.HttpOnly = true;
            });
            services.AddLogging();
            services.AddSignalR();



            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            //提供了一个IHttpContextAccessor接口，HttpContextAccessor 默认实现了它简化了访问HttpContext。
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();



            services.AddDbContext<MvcMovieContext>(options =>    // 指定要使用的数据库和连接字符串。
                    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));

            //注册单例模式，整个应用程序周期内ITodoRepository接口的示例都是TodoRepository1的一个单例实例
            services.AddSingleton<ITodoRepository, TodoRepository1>();
            //services.AddSingleton(typeof(ITodoRepository), typeof(TodoRepository1));  // 等价形式

            //注册作用域型的类型，在特定作用域内ITodoRepository的示例是TodoRepository3
            services.AddScoped<ITodoRepository, TodoRepository3>();
            //services.AddScoped(typeof(ITodoRepository), typeof(TodoRepository3));// 等价形式

            //获取该ITodoRepository实例时，每次都要实例化一次TodoRepository4类
            services.AddTransient<ITodoRepository, TodoRepository4>();
            //services.AddTransient(typeof(ITodoRepository), typeof(TodoRepository));// 等价形式

            //如果要注入的类没有接口，那你可以直接注入自身类型，比如：
            //services.AddTransient<LoggingHelper>();
            

        }

        //对各种Middleware组件进行配置的地方，一般来说，配置模块的方法都是调用类似app.UseXXX()这样的方法
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //如何不在Startup和Controller里就可以获取到HttpContext和IApplicationBuilder实例以便使用这些依赖注入服务？
            //如何获取IApplicationBuilder实例？
            //答案：在Startup里将IApplicationBuilder实例保存在一个单例中的变量上，后期全站就可以使用了。


            // 中间件是在应用启动时构造的，而不是按请求构造的，因此在每个请求过程中，中间件构造函数使用的范围内生存期服务不与其他依赖关系注入类型共享。
            app.UseMiddleware<TimeRecorderMiddleware>(); // 自定义中间件-基于工厂方法的中间件，

            app.UseRequestCulture();//自定义中间件-基于约定的中间件


            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }
            
            app.UseHttpsRedirection();
            app.UseStaticFiles(); /* 静态文件处理流程*/
            app.UseCookiePolicy();

            app.UseSession();

            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/chatHub");
            });


            //调用这些方法时，可以配置并传入响应的参数。
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

            

            /*提供了一个run方法实现类似之前版本中的handler的功能,任何Pipeline直接对Response进行响应
            app.Run(async context =>
            {
                context.Response.ContentType = "text/html";
                await context.Response.WriteAsync("Hello World!");
            });
             */
        }
    }
}
